package com.hengfoxwolf.limitrebouncedemo
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.ViewGroup
import androidx.core.view.get
import kotlin.math.abs

class LimitedReboundLayout @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null
) : ViewGroup(context, attributeSet) {

    private var viewTotalHeight = 0
    private var canMovedHeight = 0

    private var downY = 0
    private var totalMovedY = 0
    private var reboundMovedY = 0

    private var currentCondition = CONDITION0

    private var moveEnabled = false


    override fun onFinishInflate() {
        super.onFinishInflate()
        if (childCount == 0) {
            throw Exception("No child.")
        }
        if (childCount > 1) {
            throw Exception("Only supported 1 child.")
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        Log.d(TAG, "onMeasure--childCount:${childCount}")
        var measureHeight = 0
        val view = get(0)
        val viewLayoutParams = view.layoutParams as MarginLayoutParams
        measureHeight += viewLayoutParams.topMargin
        if (view is ViewGroup) {
            val childCount = view.childCount
            Log.d(TAG, "onMeasure--new--new--childCount:${childCount}")
            for (j in 0.until(childCount)) {
                val child = view[j]
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0)
                val childLayoutParams = child.layoutParams as MarginLayoutParams
                measureHeight += child.measuredHeight + childLayoutParams.topMargin
                if (j == childCount - 1) {
                    measureHeight += childLayoutParams.bottomMargin
                }
            }
        } else {
            measureChildWithMargins(view, widthMeasureSpec, 0, heightMeasureSpec, 0)
            val childLayoutParams = view.layoutParams as MarginLayoutParams
            measureHeight += view.measuredHeight + childLayoutParams.bottomMargin
        }

        Log.d(TAG, "measureHeight:${measureHeight}")
        //父view的高为边距高和所有子view高之和
        measureChildWithMargins(
            view,
            widthMeasureSpec,
            0,
            MeasureSpec.makeMeasureSpec(measureHeight, MeasureSpec.EXACTLY),
            0
        )

        setMeasuredDimension(
            MeasureSpec.getSize(widthMeasureSpec) - paddingStart - paddingEnd,
            MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom
        )
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        canMovedHeight = 0
        viewTotalHeight = 0

        val view = get(0)
        val viewLayoutParams = view.layoutParams as MarginLayoutParams
        view.layout(
            viewLayoutParams.leftMargin,
            viewLayoutParams.topMargin,
            viewLayoutParams.leftMargin + view.measuredWidth,
            viewLayoutParams.topMargin + view.measuredHeight
        )
        viewTotalHeight += viewLayoutParams.topMargin

        if (view is ViewGroup) {
            val childCount = view.childCount
            var preChildHeight = 0

            for (j in 0.until(childCount)) {
                val child = view[j]
                val childWidth = child.measuredWidth
                val childHeight = child.measuredHeight
                val childLayoutParams = child.layoutParams as MarginLayoutParams

                child.layout(
                    childLayoutParams.leftMargin,
                    preChildHeight + childLayoutParams.topMargin,
                    childLayoutParams.leftMargin + childWidth,
                    preChildHeight + childLayoutParams.topMargin + childHeight
                )

                viewTotalHeight += childHeight + childLayoutParams.topMargin + childLayoutParams.bottomMargin
                preChildHeight += childHeight + childLayoutParams.topMargin
            }
        } else {
            viewTotalHeight += view.measuredHeight + viewLayoutParams.bottomMargin
        }

        canMovedHeight = viewTotalHeight - measuredHeight
        moveEnabled = canMovedHeight > 0
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        when (ev?.action) {
            MotionEvent.ACTION_DOWN -> {
                downY = ev.y.toInt()
            }
            MotionEvent.ACTION_MOVE -> {
                val moveY = ev.y.toInt()
                val diffY = moveY - downY
                val condition = abs(diffY) >= ViewConfiguration.get(context).scaledTouchSlop
                return if (condition) {
                    downY = moveY
                    true
                } else {
                    downY = moveY
                    super.onInterceptTouchEvent(ev)
                }
            }
        }
        return super.onInterceptTouchEvent(ev)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        performClick()
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(TAG,"onTouchEvent---ACTION_DOWN")
                downY = event.y.toInt()
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(TAG,"onTouchEvent---ACTION_MOVE")
                val moveY = event.y.toInt()
                val diffY = moveY - downY
                //加个判断处理滑动逻辑  是为了防止滑动后手指手指没有抬起页面就自动回弹
                if (abs(diffY) >= ViewConfiguration.get(context).scaledTouchSlop) {
                    if (moveEnabled) {
                        if (totalMovedY == 0 && diffY > 0) {
                            Log.d(TAG,"totalMovedY == 0 && diffY > 0")
                            reboundMovedY += (diffY * FACTOR).toInt()
                            scrollTo(0, -reboundMovedY)
                            currentCondition = CONDITION0
                        } else if (totalMovedY == -canMovedHeight && diffY < 0) {
                            Log.d(TAG,"totalMovedY == -canMovedHeight && diffY < 0")
                            reboundMovedY += (diffY * FACTOR).toInt()
                            scrollTo(0, -reboundMovedY + canMovedHeight)
                            currentCondition = CONDITION1
                        } else {
                            Log.d(TAG,"=================================")
                            totalMovedY += diffY
                            totalMovedY =
                                if (diffY < 0) Math.max(totalMovedY, -canMovedHeight)
                                else Math.min(totalMovedY,0)
                            scrollTo(0, -totalMovedY)
                            reboundMovedY = 0
                            currentCondition = CONDITION2
                        }
                    } else {
                        reboundMovedY += (diffY * FACTOR).toInt()
                        scrollTo(0, -reboundMovedY)
                        currentCondition = CONDITION0
                    }
                }
                downY = moveY
            }
            MotionEvent.ACTION_UP -> {
                Log.d(TAG,"onTouchEvent---ACTION_UP")
                when (currentCondition) {
                    CONDITION0 -> {
                        Log.d(TAG,"CONDITION0")
                        scrollTo(0, 0)
                    }
                    CONDITION1 -> {
                        Log.d(TAG,"CONDITION1")
                        scrollTo(0, -totalMovedY)
                    }
                    else -> {
                        Log.d(TAG,"CONDITION2")
                        scrollTo(0, -totalMovedY)
                    }
                }
                reboundMovedY = 0
            }
        }
        return true
    }

    override fun performClick(): Boolean {
        return super.performClick()
    }

    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
        return MarginLayoutParams(context, attrs)
    }

    companion object {
        const val TAG = "LimitedReboundConstraintLayout"
        const val FACTOR = 0.42F

        const val CONDITION0 = 0x00
        const val CONDITION1 = 0x01
        const val CONDITION2 = 0x02
    }

}